package org.tinycloud.tinyjob.client.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.tinycloud.tinyjob.client.TinyJobHelper;
import org.tinycloud.tinyjob.client.utils.JsonUtils;
import org.tinycloud.tinyjob.client.utils.secure.HmacSha256Utils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * <p>
 * 主机调用安全拦截器
 * </p>
 *
 * @author liuxingyu01
 * @since 2024-04-09 12:06
 */
public class HostInvocationSecurityInterceptor implements HandlerInterceptor {
    final static Logger logger = LoggerFactory.getLogger(HostInvocationSecurityInterceptor.class);

    /**
     * 签名有效期(毫秒)，防止重放攻击，暂配置为1分钟，可根据需要修改
     */
    private static final long SIGN_EXPIRE_MILLIS = 1 * 60 * 1000;


    /*
     * 进入controller层之前拦截请求
     * 返回值：表示是否将当前的请求拦截下来  false：拦截请求，请求别终止。true：请求不被拦截，继续执行
     * Object obj:表示被拦的请求的目标对象（controller中方法）
     */

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
        String nonce = request.getHeader("nonce");
        String timestamp = request.getHeader("timestamp");
        String signature = request.getHeader("signature");

        if (nonce == null || nonce.isEmpty()) {
            outWrite(response);
        }

        // 1. 验证时间戳是否在有效期内，防止重放攻击
        long currentTime = System.currentTimeMillis();
        if (timestamp == null || timestamp.isEmpty()) {
            outWrite(response);
        } else {
            if (Math.abs(currentTime - Long.parseLong(timestamp)) > SIGN_EXPIRE_MILLIS) {
                outWrite(response);
            }
        }

        // 2. 验证签名
        String paramStr = nonce + "#" + timestamp + "#";
        String accessToken = TinyJobHelper.CONFIG_CACHE.get(TinyJobHelper.CONFIG_CACHE_KEY).getAccessToken();
        if (signature == null || !HmacSha256Utils.verify(accessToken, paramStr, signature)) {
            outWrite(response);
        }

        String jobId = request.getHeader("jobId");
        String jobLogId = request.getHeader("jobLogId");
        JobSubject jobSubject = new JobSubject();
        jobSubject.setJobId(StringUtils.hasLength(jobId) ? Long.parseLong(jobId) : null);
        jobSubject.setJobLogId(jobLogId);
        JobSubjectHolder.setJobSubject(jobSubject);

        // 合格，放行
        return true;
    }

    /*
     * 处理请求完成后视图渲染之前的处理操作
     * 通过ModelAndView参数改变显示的视图，或发往视图的方法
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
    }

    /*
     * 视图渲染之后的操作
     */
    @Override
    public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception {
        JobSubjectHolder.clear();
    }

    private void outWrite(HttpServletResponse response) throws IOException {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 401);
        result.put("message", "主机调用会话认证失败");
        response.setContentType("application/json");
        PrintWriter out = response.getWriter();
        out.write(JsonUtils.writeValueAsString(result));
        out.close();
    }
}
